tableauserverclient 0.33__py3-none-any.whl → 0.35__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 +33 -23
- tableauserverclient/{_version.py → bin/_version.py} +3 -3
- tableauserverclient/config.py +5 -3
- tableauserverclient/models/column_item.py +1 -1
- tableauserverclient/models/connection_credentials.py +18 -2
- tableauserverclient/models/connection_item.py +44 -6
- tableauserverclient/models/custom_view_item.py +78 -11
- 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 +54 -9
- tableauserverclient/models/flow_run_item.py +3 -3
- tableauserverclient/models/group_item.py +44 -4
- tableauserverclient/models/groupset_item.py +4 -4
- tableauserverclient/models/interval_item.py +9 -9
- tableauserverclient/models/job_item.py +73 -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 +73 -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 +34 -4
- tableauserverclient/models/user_item.py +47 -17
- tableauserverclient/models/view_item.py +66 -13
- tableauserverclient/models/virtual_connection_item.py +6 -5
- tableauserverclient/models/webhook_item.py +39 -6
- tableauserverclient/models/workbook_item.py +116 -13
- tableauserverclient/namespace.py +1 -1
- tableauserverclient/server/__init__.py +2 -1
- tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
- tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
- 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 +61 -62
- 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 +344 -29
- tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
- tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
- tableauserverclient/server/endpoint/jobs_endpoint.py +116 -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 +681 -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 +86 -8
- tableauserverclient/server/endpoint/users_endpoint.py +366 -19
- tableauserverclient/server/endpoint/views_endpoint.py +262 -20
- tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
- tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
- tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
- tableauserverclient/server/filter.py +2 -2
- tableauserverclient/server/pager.py +29 -6
- tableauserverclient/server/query.py +68 -19
- tableauserverclient/server/request_factory.py +57 -37
- tableauserverclient/server/request_options.py +243 -141
- tableauserverclient/server/server.py +76 -10
- tableauserverclient/server/sort.py +16 -2
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
- tableauserverclient-0.35.dist-info/RECORD +106 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
- tableauserverclient-0.33.dist-info/RECORD +0 -106
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
- {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/top_level.txt +0 -0
|
@@ -5,7 +5,8 @@ import logging
|
|
|
5
5
|
import os
|
|
6
6
|
from contextlib import closing
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import Optional, TYPE_CHECKING, Union
|
|
9
|
+
from collections.abc import Iterable
|
|
9
10
|
|
|
10
11
|
from tableauserverclient.helpers.headers import fix_filename
|
|
11
12
|
|
|
@@ -53,18 +54,37 @@ PathOrFileW = Union[FilePath, FileObjectW]
|
|
|
53
54
|
|
|
54
55
|
class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
55
56
|
def __init__(self, parent_srv):
|
|
56
|
-
super(
|
|
57
|
+
super().__init__(parent_srv)
|
|
57
58
|
self._resource_tagger = _ResourceTagger(parent_srv)
|
|
58
59
|
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
|
|
59
60
|
self._data_quality_warnings = _DataQualityWarningEndpoint(self.parent_srv, "flow")
|
|
60
61
|
|
|
61
62
|
@property
|
|
62
63
|
def baseurl(self) -> str:
|
|
63
|
-
return "{
|
|
64
|
+
return f"{self.parent_srv.baseurl}/sites/{self.parent_srv.site_id}/flows"
|
|
64
65
|
|
|
65
66
|
# Get all flows
|
|
66
67
|
@api(version="3.3")
|
|
67
|
-
def get(self, req_options: Optional["RequestOptions"] = None) ->
|
|
68
|
+
def get(self, req_options: Optional["RequestOptions"] = None) -> tuple[list[FlowItem], PaginationItem]:
|
|
69
|
+
"""
|
|
70
|
+
Get all flows on site. Returns a tuple of all flow items and pagination item.
|
|
71
|
+
This method is paginated, and returns one page of items per call. The
|
|
72
|
+
request options can be used to specify the page number, page size, as
|
|
73
|
+
well as sorting and filtering options.
|
|
74
|
+
|
|
75
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#query_flows_for_site
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
req_options: Optional[RequestOptions]
|
|
80
|
+
An optional Request Options object that can be used to specify
|
|
81
|
+
sorting, filtering, and pagination options.
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
tuple[list[FlowItem], PaginationItem]
|
|
86
|
+
A tuple of a list of flow items and a pagination item.
|
|
87
|
+
"""
|
|
68
88
|
logger.info("Querying all flows on site")
|
|
69
89
|
url = self.baseurl
|
|
70
90
|
server_response = self.get_request(url, req_options)
|
|
@@ -75,17 +95,53 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
75
95
|
# Get 1 flow by id
|
|
76
96
|
@api(version="3.3")
|
|
77
97
|
def get_by_id(self, flow_id: str) -> FlowItem:
|
|
98
|
+
"""
|
|
99
|
+
Get a single flow by id.
|
|
100
|
+
|
|
101
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#query_flow
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
flow_id: str
|
|
106
|
+
The id of the flow to retrieve.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
FlowItem
|
|
111
|
+
The flow item that was retrieved.
|
|
112
|
+
"""
|
|
78
113
|
if not flow_id:
|
|
79
114
|
error = "Flow ID undefined."
|
|
80
115
|
raise ValueError(error)
|
|
81
|
-
logger.info("Querying single flow (ID: {
|
|
82
|
-
url = "{
|
|
116
|
+
logger.info(f"Querying single flow (ID: {flow_id})")
|
|
117
|
+
url = f"{self.baseurl}/{flow_id}"
|
|
83
118
|
server_response = self.get_request(url)
|
|
84
119
|
return FlowItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
85
120
|
|
|
86
121
|
# Populate flow item's connections
|
|
87
122
|
@api(version="3.3")
|
|
88
123
|
def populate_connections(self, flow_item: FlowItem) -> None:
|
|
124
|
+
"""
|
|
125
|
+
Populate the connections for a flow item. This method will make a
|
|
126
|
+
request to the Tableau Server to get the connections associated with
|
|
127
|
+
the flow item and populate the connections property of the flow item.
|
|
128
|
+
|
|
129
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#query_flow_connections
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
flow_item: FlowItem
|
|
134
|
+
The flow item to populate connections for.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
None
|
|
139
|
+
|
|
140
|
+
Raises
|
|
141
|
+
------
|
|
142
|
+
MissingRequiredFieldError
|
|
143
|
+
If the flow item does not have an ID.
|
|
144
|
+
"""
|
|
89
145
|
if not flow_item.id:
|
|
90
146
|
error = "Flow item missing ID. Flow must be retrieved from server first."
|
|
91
147
|
raise MissingRequiredFieldError(error)
|
|
@@ -94,10 +150,10 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
94
150
|
return self._get_flow_connections(flow_item)
|
|
95
151
|
|
|
96
152
|
flow_item._set_connections(connections_fetcher)
|
|
97
|
-
logger.info("Populated connections for flow (ID: {
|
|
153
|
+
logger.info(f"Populated connections for flow (ID: {flow_item.id})")
|
|
98
154
|
|
|
99
|
-
def _get_flow_connections(self, flow_item, req_options: Optional["RequestOptions"] = None) ->
|
|
100
|
-
url = "{
|
|
155
|
+
def _get_flow_connections(self, flow_item, req_options: Optional["RequestOptions"] = None) -> list[ConnectionItem]:
|
|
156
|
+
url = f"{self.baseurl}/{flow_item.id}/connections"
|
|
101
157
|
server_response = self.get_request(url, req_options)
|
|
102
158
|
connections = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)
|
|
103
159
|
return connections
|
|
@@ -105,20 +161,68 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
105
161
|
# Delete 1 flow by id
|
|
106
162
|
@api(version="3.3")
|
|
107
163
|
def delete(self, flow_id: str) -> None:
|
|
164
|
+
"""
|
|
165
|
+
Delete a single flow by id.
|
|
166
|
+
|
|
167
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#delete_flow
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
flow_id: str
|
|
172
|
+
The id of the flow to delete.
|
|
173
|
+
|
|
174
|
+
Returns
|
|
175
|
+
-------
|
|
176
|
+
None
|
|
177
|
+
|
|
178
|
+
Raises
|
|
179
|
+
------
|
|
180
|
+
ValueError
|
|
181
|
+
If the flow_id is not defined.
|
|
182
|
+
"""
|
|
108
183
|
if not flow_id:
|
|
109
184
|
error = "Flow ID undefined."
|
|
110
185
|
raise ValueError(error)
|
|
111
|
-
url = "{
|
|
186
|
+
url = f"{self.baseurl}/{flow_id}"
|
|
112
187
|
self.delete_request(url)
|
|
113
|
-
logger.info("Deleted single flow (ID: {
|
|
188
|
+
logger.info(f"Deleted single flow (ID: {flow_id})")
|
|
114
189
|
|
|
115
190
|
# Download 1 flow by id
|
|
116
191
|
@api(version="3.3")
|
|
117
192
|
def download(self, flow_id: str, filepath: Optional[PathOrFileW] = None) -> PathOrFileW:
|
|
193
|
+
"""
|
|
194
|
+
Download a single flow by id. The flow will be downloaded to the
|
|
195
|
+
specified file path. If no file path is specified, the flow will be
|
|
196
|
+
downloaded to the current working directory.
|
|
197
|
+
|
|
198
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#download_flow
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
flow_id: str
|
|
203
|
+
The id of the flow to download.
|
|
204
|
+
|
|
205
|
+
filepath: Optional[PathOrFileW]
|
|
206
|
+
The file path to download the flow to. This can be a file path or
|
|
207
|
+
a file object. If a file object is passed, the flow will be written
|
|
208
|
+
to the file object. If a file path is passed, the flow will be
|
|
209
|
+
written to the file path. If no file path is specified, the flow
|
|
210
|
+
will be downloaded to the current working directory.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
PathOrFileW
|
|
215
|
+
The file path or file object that the flow was downloaded to.
|
|
216
|
+
|
|
217
|
+
Raises
|
|
218
|
+
------
|
|
219
|
+
ValueError
|
|
220
|
+
If the flow_id is not defined.
|
|
221
|
+
"""
|
|
118
222
|
if not flow_id:
|
|
119
223
|
error = "Flow ID undefined."
|
|
120
224
|
raise ValueError(error)
|
|
121
|
-
url = "{
|
|
225
|
+
url = f"{self.baseurl}/{flow_id}/content"
|
|
122
226
|
|
|
123
227
|
with closing(self.get_request(url, parameters={"stream": True})) as server_response:
|
|
124
228
|
m = Message()
|
|
@@ -137,12 +241,27 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
137
241
|
f.write(chunk)
|
|
138
242
|
return_path = os.path.abspath(download_path)
|
|
139
243
|
|
|
140
|
-
logger.info("Downloaded flow to {
|
|
244
|
+
logger.info(f"Downloaded flow to {return_path} (ID: {flow_id})")
|
|
141
245
|
return return_path
|
|
142
246
|
|
|
143
247
|
# Update flow
|
|
144
248
|
@api(version="3.3")
|
|
145
249
|
def update(self, flow_item: FlowItem) -> FlowItem:
|
|
250
|
+
"""
|
|
251
|
+
Updates the flow owner, project, description, and/or tags.
|
|
252
|
+
|
|
253
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#update_flow
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
flow_item: FlowItem
|
|
258
|
+
The flow item to update.
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
FlowItem
|
|
263
|
+
The updated flow item.
|
|
264
|
+
"""
|
|
146
265
|
if not flow_item.id:
|
|
147
266
|
error = "Flow item missing ID. Flow must be retrieved from server first."
|
|
148
267
|
raise MissingRequiredFieldError(error)
|
|
@@ -150,28 +269,62 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
150
269
|
self._resource_tagger.update_tags(self.baseurl, flow_item)
|
|
151
270
|
|
|
152
271
|
# Update the flow itself
|
|
153
|
-
url = "{
|
|
272
|
+
url = f"{self.baseurl}/{flow_item.id}"
|
|
154
273
|
update_req = RequestFactory.Flow.update_req(flow_item)
|
|
155
274
|
server_response = self.put_request(url, update_req)
|
|
156
|
-
logger.info("Updated flow item (ID: {
|
|
275
|
+
logger.info(f"Updated flow item (ID: {flow_item.id})")
|
|
157
276
|
updated_flow = copy.copy(flow_item)
|
|
158
277
|
return updated_flow._parse_common_elements(server_response.content, self.parent_srv.namespace)
|
|
159
278
|
|
|
160
279
|
# Update flow connections
|
|
161
280
|
@api(version="3.3")
|
|
162
281
|
def update_connection(self, flow_item: FlowItem, connection_item: ConnectionItem) -> ConnectionItem:
|
|
163
|
-
|
|
282
|
+
"""
|
|
283
|
+
Update a connection item for a flow item. This method will update the
|
|
284
|
+
connection details for the connection item associated with the flow.
|
|
285
|
+
|
|
286
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#update_flow_connection
|
|
287
|
+
|
|
288
|
+
Parameters
|
|
289
|
+
----------
|
|
290
|
+
flow_item: FlowItem
|
|
291
|
+
The flow item that the connection is associated with.
|
|
292
|
+
|
|
293
|
+
connection_item: ConnectionItem
|
|
294
|
+
The connection item to update.
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
ConnectionItem
|
|
299
|
+
The updated connection item.
|
|
300
|
+
"""
|
|
301
|
+
url = f"{self.baseurl}/{flow_item.id}/connections/{connection_item.id}"
|
|
164
302
|
|
|
165
303
|
update_req = RequestFactory.Connection.update_req(connection_item)
|
|
166
304
|
server_response = self.put_request(url, update_req)
|
|
167
305
|
connection = ConnectionItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
168
306
|
|
|
169
|
-
logger.info("Updated flow item (ID: {
|
|
307
|
+
logger.info(f"Updated flow item (ID: {flow_item.id} & connection item {connection_item.id}")
|
|
170
308
|
return connection
|
|
171
309
|
|
|
172
310
|
@api(version="3.3")
|
|
173
311
|
def refresh(self, flow_item: FlowItem) -> JobItem:
|
|
174
|
-
|
|
312
|
+
"""
|
|
313
|
+
Runs the flow to refresh the data.
|
|
314
|
+
|
|
315
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#run_flow_now
|
|
316
|
+
|
|
317
|
+
Parameters
|
|
318
|
+
----------
|
|
319
|
+
flow_item: FlowItem
|
|
320
|
+
The flow item to refresh.
|
|
321
|
+
|
|
322
|
+
Returns
|
|
323
|
+
-------
|
|
324
|
+
JobItem
|
|
325
|
+
The job item that was created to refresh the flow.
|
|
326
|
+
"""
|
|
327
|
+
url = f"{self.baseurl}/{flow_item.id}/run"
|
|
175
328
|
empty_req = RequestFactory.Empty.empty_req()
|
|
176
329
|
server_response = self.post_request(url, empty_req)
|
|
177
330
|
new_job = JobItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
@@ -180,8 +333,37 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
180
333
|
# Publish flow
|
|
181
334
|
@api(version="3.3")
|
|
182
335
|
def publish(
|
|
183
|
-
self, flow_item: FlowItem, file: PathOrFileR, mode: str, connections: Optional[
|
|
336
|
+
self, flow_item: FlowItem, file: PathOrFileR, mode: str, connections: Optional[list[ConnectionItem]] = None
|
|
184
337
|
) -> FlowItem:
|
|
338
|
+
"""
|
|
339
|
+
Publishes a flow to the Tableau Server.
|
|
340
|
+
|
|
341
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#publish_flow
|
|
342
|
+
|
|
343
|
+
Parameters
|
|
344
|
+
----------
|
|
345
|
+
flow_item: FlowItem
|
|
346
|
+
The flow item to publish. This item must have a project_id and name
|
|
347
|
+
defined.
|
|
348
|
+
|
|
349
|
+
file: PathOrFileR
|
|
350
|
+
The file path or file object to publish. This can be a .tfl or .tflx
|
|
351
|
+
|
|
352
|
+
mode: str
|
|
353
|
+
The publish mode. This can be "Overwrite" or "CreatNew". If the
|
|
354
|
+
mode is "Overwrite", the flow will be overwritten if it already
|
|
355
|
+
exists. If the mode is "CreateNew", a new flow will be created with
|
|
356
|
+
the same name as the flow item.
|
|
357
|
+
|
|
358
|
+
connections: Optional[list[ConnectionItem]]
|
|
359
|
+
A list of connection items to publish with the flow. If the flow
|
|
360
|
+
contains connections, they must be included in this list.
|
|
361
|
+
|
|
362
|
+
Returns
|
|
363
|
+
-------
|
|
364
|
+
FlowItem
|
|
365
|
+
The flow item that was published.
|
|
366
|
+
"""
|
|
185
367
|
if not mode or not hasattr(self.parent_srv.PublishMode, mode):
|
|
186
368
|
error = "Invalid mode defined."
|
|
187
369
|
raise ValueError(error)
|
|
@@ -189,7 +371,7 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
189
371
|
if isinstance(file, (str, os.PathLike)):
|
|
190
372
|
if not os.path.isfile(file):
|
|
191
373
|
error = "File path does not lead to an existing file."
|
|
192
|
-
raise
|
|
374
|
+
raise OSError(error)
|
|
193
375
|
|
|
194
376
|
filename = os.path.basename(file)
|
|
195
377
|
file_extension = os.path.splitext(filename)[1][1:]
|
|
@@ -213,30 +395,30 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
213
395
|
elif file_type == "xml":
|
|
214
396
|
file_extension = "tfl"
|
|
215
397
|
else:
|
|
216
|
-
error = "Unsupported file type {}!"
|
|
398
|
+
error = f"Unsupported file type {file_type}!"
|
|
217
399
|
raise ValueError(error)
|
|
218
400
|
|
|
219
401
|
# Generate filename for file object.
|
|
220
402
|
# This is needed when publishing the flow in a single request
|
|
221
|
-
filename = "{}.{}"
|
|
403
|
+
filename = f"{flow_item.name}.{file_extension}"
|
|
222
404
|
file_size = get_file_object_size(file)
|
|
223
405
|
|
|
224
406
|
else:
|
|
225
407
|
raise TypeError("file should be a filepath or file object.")
|
|
226
408
|
|
|
227
409
|
# Construct the url with the defined mode
|
|
228
|
-
url = "{
|
|
410
|
+
url = f"{self.baseurl}?flowType={file_extension}"
|
|
229
411
|
if mode == self.parent_srv.PublishMode.Overwrite or mode == self.parent_srv.PublishMode.Append:
|
|
230
|
-
url += "&{
|
|
412
|
+
url += f"&{mode.lower()}=true"
|
|
231
413
|
|
|
232
414
|
# Determine if chunking is required (64MB is the limit for single upload method)
|
|
233
415
|
if file_size >= FILESIZE_LIMIT:
|
|
234
|
-
logger.info("Publishing {
|
|
416
|
+
logger.info(f"Publishing {filename} to server with chunking method (flow over 64MB)")
|
|
235
417
|
upload_session_id = self.parent_srv.fileuploads.upload(file)
|
|
236
|
-
url = "{
|
|
418
|
+
url = f"{url}&uploadSessionId={upload_session_id}"
|
|
237
419
|
xml_request, content_type = RequestFactory.Flow.publish_req_chunked(flow_item, connections)
|
|
238
420
|
else:
|
|
239
|
-
logger.info("Publishing {
|
|
421
|
+
logger.info(f"Publishing {filename} to server")
|
|
240
422
|
|
|
241
423
|
if isinstance(file, (str, Path)):
|
|
242
424
|
with open(file, "rb") as f:
|
|
@@ -259,42 +441,175 @@ class Flows(QuerysetEndpoint[FlowItem], TaggingMixin[FlowItem]):
|
|
|
259
441
|
raise err
|
|
260
442
|
else:
|
|
261
443
|
new_flow = FlowItem.from_response(server_response.content, self.parent_srv.namespace)[0]
|
|
262
|
-
logger.info("Published {
|
|
444
|
+
logger.info(f"Published {filename} (ID: {new_flow.id})")
|
|
263
445
|
return new_flow
|
|
264
446
|
|
|
265
447
|
@api(version="3.3")
|
|
266
448
|
def populate_permissions(self, item: FlowItem) -> None:
|
|
449
|
+
"""
|
|
450
|
+
Populate the permissions for a flow item. This method will make a
|
|
451
|
+
request to the Tableau Server to get the permissions associated with
|
|
452
|
+
the flow item and populate the permissions property of the flow item.
|
|
453
|
+
|
|
454
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#query_flow_permissions
|
|
455
|
+
|
|
456
|
+
Parameters
|
|
457
|
+
----------
|
|
458
|
+
item: FlowItem
|
|
459
|
+
The flow item to populate permissions for.
|
|
460
|
+
|
|
461
|
+
Returns
|
|
462
|
+
-------
|
|
463
|
+
None
|
|
464
|
+
"""
|
|
267
465
|
self._permissions.populate(item)
|
|
268
466
|
|
|
269
467
|
@api(version="3.3")
|
|
270
468
|
def update_permissions(self, item: FlowItem, permission_item: Iterable["PermissionsRule"]) -> None:
|
|
469
|
+
"""
|
|
470
|
+
Update the permissions for a flow item. This method will update the
|
|
471
|
+
permissions for the flow item. The permissions must be a list of
|
|
472
|
+
permissions rules. Will overwrite all existing permissions.
|
|
473
|
+
|
|
474
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_permissions.htm#replace_permissions_for_content
|
|
475
|
+
|
|
476
|
+
Parameters
|
|
477
|
+
----------
|
|
478
|
+
item: FlowItem
|
|
479
|
+
The flow item to update permissions for.
|
|
480
|
+
|
|
481
|
+
permission_item: Iterable[PermissionsRule]
|
|
482
|
+
The permissions rules to update.
|
|
483
|
+
|
|
484
|
+
Returns
|
|
485
|
+
-------
|
|
486
|
+
None
|
|
487
|
+
"""
|
|
271
488
|
self._permissions.update(item, permission_item)
|
|
272
489
|
|
|
273
490
|
@api(version="3.3")
|
|
274
491
|
def delete_permission(self, item: FlowItem, capability_item: "PermissionsRule") -> None:
|
|
492
|
+
"""
|
|
493
|
+
Delete a permission for a flow item. This method will delete only the
|
|
494
|
+
specified permission for the flow item.
|
|
495
|
+
|
|
496
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#delete_flow_permission
|
|
497
|
+
|
|
498
|
+
Parameters
|
|
499
|
+
----------
|
|
500
|
+
item: FlowItem
|
|
501
|
+
The flow item to delete the permission from.
|
|
502
|
+
|
|
503
|
+
capability_item: PermissionsRule
|
|
504
|
+
The permission to delete.
|
|
505
|
+
|
|
506
|
+
Returns
|
|
507
|
+
-------
|
|
508
|
+
None
|
|
509
|
+
"""
|
|
275
510
|
self._permissions.delete(item, capability_item)
|
|
276
511
|
|
|
277
512
|
@api(version="3.5")
|
|
278
513
|
def populate_dqw(self, item: FlowItem) -> None:
|
|
514
|
+
"""
|
|
515
|
+
Get information about Data Quality Warnings for a flow item.
|
|
516
|
+
|
|
517
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#query_dqws
|
|
518
|
+
|
|
519
|
+
Parameters
|
|
520
|
+
----------
|
|
521
|
+
item: FlowItem
|
|
522
|
+
The flow item to populate data quality warnings for.
|
|
523
|
+
|
|
524
|
+
Returns
|
|
525
|
+
-------
|
|
526
|
+
None
|
|
527
|
+
"""
|
|
279
528
|
self._data_quality_warnings.populate(item)
|
|
280
529
|
|
|
281
530
|
@api(version="3.5")
|
|
282
531
|
def update_dqw(self, item: FlowItem, warning: "DQWItem") -> None:
|
|
532
|
+
"""
|
|
533
|
+
Update the warning type, status, and message of a data quality warning
|
|
534
|
+
|
|
535
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#update_dqw
|
|
536
|
+
|
|
537
|
+
Parameters
|
|
538
|
+
----------
|
|
539
|
+
item: FlowItem
|
|
540
|
+
The flow item to update data quality warnings for.
|
|
541
|
+
|
|
542
|
+
warning: DQWItem
|
|
543
|
+
The data quality warning to update.
|
|
544
|
+
|
|
545
|
+
Returns
|
|
546
|
+
-------
|
|
547
|
+
None
|
|
548
|
+
"""
|
|
283
549
|
return self._data_quality_warnings.update(item, warning)
|
|
284
550
|
|
|
285
551
|
@api(version="3.5")
|
|
286
552
|
def add_dqw(self, item: FlowItem, warning: "DQWItem") -> None:
|
|
553
|
+
"""
|
|
554
|
+
Add a data quality warning to a flow.
|
|
555
|
+
|
|
556
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#add_dqw
|
|
557
|
+
|
|
558
|
+
Parameters
|
|
559
|
+
----------
|
|
560
|
+
item: FlowItem
|
|
561
|
+
The flow item to add data quality warnings to.
|
|
562
|
+
|
|
563
|
+
warning: DQWItem
|
|
564
|
+
The data quality warning to add.
|
|
565
|
+
|
|
566
|
+
Returns
|
|
567
|
+
-------
|
|
568
|
+
None
|
|
569
|
+
"""
|
|
287
570
|
return self._data_quality_warnings.add(item, warning)
|
|
288
571
|
|
|
289
572
|
@api(version="3.5")
|
|
290
573
|
def delete_dqw(self, item: FlowItem) -> None:
|
|
574
|
+
"""
|
|
575
|
+
Delete all data quality warnings for a flow.
|
|
576
|
+
|
|
577
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_metadata.htm#delete_dqws
|
|
578
|
+
|
|
579
|
+
Parameters
|
|
580
|
+
----------
|
|
581
|
+
item: FlowItem
|
|
582
|
+
The flow item to delete data quality warnings from.
|
|
583
|
+
|
|
584
|
+
Returns
|
|
585
|
+
-------
|
|
586
|
+
None
|
|
587
|
+
"""
|
|
291
588
|
self._data_quality_warnings.clear(item)
|
|
292
589
|
|
|
293
590
|
# a convenience method
|
|
294
591
|
@api(version="3.3")
|
|
295
592
|
def schedule_flow_run(
|
|
296
593
|
self, schedule_id: str, item: FlowItem
|
|
297
|
-
) ->
|
|
594
|
+
) -> list["AddResponse"]: # actually should return a task
|
|
595
|
+
"""
|
|
596
|
+
Schedule a flow to run on an existing schedule.
|
|
597
|
+
|
|
598
|
+
REST API: https://help.tableau.com/current/api/rest_api/en-us/REST/rest_api_ref_flow.htm#add_flow_task_to_schedule
|
|
599
|
+
|
|
600
|
+
Parameters
|
|
601
|
+
----------
|
|
602
|
+
schedule_id: str
|
|
603
|
+
The id of the schedule to add the flow to.
|
|
604
|
+
|
|
605
|
+
item: FlowItem
|
|
606
|
+
The flow item to add to the schedule.
|
|
607
|
+
|
|
608
|
+
Returns
|
|
609
|
+
-------
|
|
610
|
+
list[AddResponse]
|
|
611
|
+
The response from the server.
|
|
612
|
+
"""
|
|
298
613
|
return self.parent_srv.schedules.add_to_schedule(schedule_id, flow=item)
|
|
299
614
|
|
|
300
615
|
def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySet[FlowItem]:
|