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
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import xml.etree.ElementTree as ET
|
|
2
|
-
from typing import Set
|
|
3
2
|
|
|
4
3
|
from defusedxml.ElementTree import fromstring
|
|
5
4
|
|
|
6
5
|
|
|
7
|
-
class TagItem
|
|
6
|
+
class TagItem:
|
|
8
7
|
@classmethod
|
|
9
|
-
def from_response(cls, resp: bytes, ns) ->
|
|
8
|
+
def from_response(cls, resp: bytes, ns) -> set[str]:
|
|
10
9
|
return cls.from_xml_element(fromstring(resp), ns)
|
|
11
10
|
|
|
12
11
|
@classmethod
|
|
13
|
-
def from_xml_element(cls, parsed_response: ET.Element, ns) ->
|
|
12
|
+
def from_xml_element(cls, parsed_response: ET.Element, ns) -> set[str]:
|
|
14
13
|
all_tags = set()
|
|
15
14
|
tag_elem = parsed_response.findall(".//t:tag", namespaces=ns)
|
|
16
15
|
for tag_xml in tag_elem:
|
|
@@ -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
|
|
|
@@ -8,7 +8,37 @@ from tableauserverclient.models.schedule_item import ScheduleItem
|
|
|
8
8
|
from tableauserverclient.models.target import Target
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class TaskItem
|
|
11
|
+
class TaskItem:
|
|
12
|
+
"""
|
|
13
|
+
Represents a task item in Tableau Server. To create new tasks, see Schedules.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
id_ : str
|
|
18
|
+
The ID of the task.
|
|
19
|
+
|
|
20
|
+
task_type : str
|
|
21
|
+
Type of task. See TaskItem.Type for possible values.
|
|
22
|
+
|
|
23
|
+
priority : int
|
|
24
|
+
The priority of the task on the server.
|
|
25
|
+
|
|
26
|
+
consecutive_failed_count : int
|
|
27
|
+
The number of consecutive times the task has failed.
|
|
28
|
+
|
|
29
|
+
schedule_id : str, optional
|
|
30
|
+
The ID of the schedule that the task is associated with.
|
|
31
|
+
|
|
32
|
+
schedule_item : ScheduleItem, optional
|
|
33
|
+
The schedule item that the task is associated with.
|
|
34
|
+
|
|
35
|
+
last_run_at : datetime, optional
|
|
36
|
+
The last time the task was run.
|
|
37
|
+
|
|
38
|
+
target : Target, optional
|
|
39
|
+
The target of the task. This can be a workbook or a datasource.
|
|
40
|
+
"""
|
|
41
|
+
|
|
12
42
|
class Type:
|
|
13
43
|
ExtractRefresh = "extractRefresh"
|
|
14
44
|
DataAcceleration = "dataAcceleration"
|
|
@@ -48,9 +78,9 @@ class TaskItem(object):
|
|
|
48
78
|
)
|
|
49
79
|
|
|
50
80
|
@classmethod
|
|
51
|
-
def from_response(cls, xml, ns, task_type=Type.ExtractRefresh) ->
|
|
81
|
+
def from_response(cls, xml, ns, task_type=Type.ExtractRefresh) -> list["TaskItem"]:
|
|
52
82
|
parsed_response = fromstring(xml)
|
|
53
|
-
all_tasks_xml = parsed_response.findall(".//t:task/t:{}"
|
|
83
|
+
all_tasks_xml = parsed_response.findall(f".//t:task/t:{task_type}", namespaces=ns)
|
|
54
84
|
|
|
55
85
|
all_tasks = (TaskItem._parse_element(x, ns) for x in all_tasks_xml)
|
|
56
86
|
|
|
@@ -2,7 +2,7 @@ import io
|
|
|
2
2
|
import xml.etree.ElementTree as ET
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from enum import IntEnum
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Optional, TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from defusedxml.ElementTree import fromstring
|
|
8
8
|
|
|
@@ -18,10 +18,35 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from tableauserverclient.server import Pager
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class UserItem
|
|
21
|
+
class UserItem:
|
|
22
|
+
"""
|
|
23
|
+
The UserItem class contains the members or attributes for the view
|
|
24
|
+
resources on Tableau Server. The UserItem class defines the information you
|
|
25
|
+
can request or query from Tableau Server. The class attributes correspond
|
|
26
|
+
to the attributes of a server request or response payload.
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
name: str
|
|
32
|
+
The name of the user.
|
|
33
|
+
|
|
34
|
+
site_role: str
|
|
35
|
+
The role of the user on the site.
|
|
36
|
+
|
|
37
|
+
auth_setting: str
|
|
38
|
+
Required attribute for Tableau Cloud. How the user autenticates to the
|
|
39
|
+
server.
|
|
40
|
+
"""
|
|
41
|
+
|
|
22
42
|
tag_name: str = "user"
|
|
23
43
|
|
|
24
44
|
class Roles:
|
|
45
|
+
"""
|
|
46
|
+
The Roles class contains the possible roles for a user on Tableau
|
|
47
|
+
Server.
|
|
48
|
+
"""
|
|
49
|
+
|
|
25
50
|
Interactor = "Interactor"
|
|
26
51
|
Publisher = "Publisher"
|
|
27
52
|
ServerAdministrator = "ServerAdministrator"
|
|
@@ -43,6 +68,11 @@ class UserItem(object):
|
|
|
43
68
|
SupportUser = "SupportUser"
|
|
44
69
|
|
|
45
70
|
class Auth:
|
|
71
|
+
"""
|
|
72
|
+
The Auth class contains the possible authentication settings for a user
|
|
73
|
+
on Tableau Cloud.
|
|
74
|
+
"""
|
|
75
|
+
|
|
46
76
|
OpenID = "OpenID"
|
|
47
77
|
SAML = "SAML"
|
|
48
78
|
TableauIDWithMFA = "TableauIDWithMFA"
|
|
@@ -57,7 +87,7 @@ class UserItem(object):
|
|
|
57
87
|
self._id: Optional[str] = None
|
|
58
88
|
self._last_login: Optional[datetime] = None
|
|
59
89
|
self._workbooks = None
|
|
60
|
-
self._favorites: Optional[
|
|
90
|
+
self._favorites: Optional[dict[str, list]] = None
|
|
61
91
|
self._groups = None
|
|
62
92
|
self.email: Optional[str] = None
|
|
63
93
|
self.fullname: Optional[str] = None
|
|
@@ -69,7 +99,7 @@ class UserItem(object):
|
|
|
69
99
|
|
|
70
100
|
def __str__(self) -> str:
|
|
71
101
|
str_site_role = self.site_role or "None"
|
|
72
|
-
return "<User {} name={} role={}>"
|
|
102
|
+
return f"<User {self.id} name={self.name} role={str_site_role}>"
|
|
73
103
|
|
|
74
104
|
def __repr__(self):
|
|
75
105
|
return self.__str__() + " { " + ", ".join(" % s: % s" % item for item in vars(self).items()) + "}"
|
|
@@ -141,7 +171,7 @@ class UserItem(object):
|
|
|
141
171
|
return self._workbooks()
|
|
142
172
|
|
|
143
173
|
@property
|
|
144
|
-
def favorites(self) ->
|
|
174
|
+
def favorites(self) -> dict[str, list]:
|
|
145
175
|
if self._favorites is None:
|
|
146
176
|
error = "User item must be populated with favorites first."
|
|
147
177
|
raise UnpopulatedPropertyError(error)
|
|
@@ -210,12 +240,12 @@ class UserItem(object):
|
|
|
210
240
|
self._domain_name = domain_name
|
|
211
241
|
|
|
212
242
|
@classmethod
|
|
213
|
-
def from_response(cls, resp, ns) ->
|
|
243
|
+
def from_response(cls, resp, ns) -> list["UserItem"]:
|
|
214
244
|
element_name = ".//t:user"
|
|
215
245
|
return cls._parse_xml(element_name, resp, ns)
|
|
216
246
|
|
|
217
247
|
@classmethod
|
|
218
|
-
def from_response_as_owner(cls, resp, ns) ->
|
|
248
|
+
def from_response_as_owner(cls, resp, ns) -> list["UserItem"]:
|
|
219
249
|
element_name = ".//t:owner"
|
|
220
250
|
return cls._parse_xml(element_name, resp, ns)
|
|
221
251
|
|
|
@@ -283,7 +313,7 @@ class UserItem(object):
|
|
|
283
313
|
domain_name,
|
|
284
314
|
)
|
|
285
315
|
|
|
286
|
-
class CSVImport
|
|
316
|
+
class CSVImport:
|
|
287
317
|
"""
|
|
288
318
|
This class includes hardcoded options and logic for the CSV file format defined for user import
|
|
289
319
|
https://help.tableau.com/current/server/en-us/users_import.htm
|
|
@@ -308,7 +338,7 @@ class UserItem(object):
|
|
|
308
338
|
if line is None or line is False or line == "\n" or line == "":
|
|
309
339
|
return None
|
|
310
340
|
line = line.strip().lower()
|
|
311
|
-
values:
|
|
341
|
+
values: list[str] = list(map(str.strip, line.split(",")))
|
|
312
342
|
user = UserItem(values[UserItem.CSVImport.ColumnType.USERNAME])
|
|
313
343
|
if len(values) > 1:
|
|
314
344
|
if len(values) > UserItem.CSVImport.ColumnType.MAX:
|
|
@@ -337,7 +367,7 @@ class UserItem(object):
|
|
|
337
367
|
# Read through an entire CSV file meant for user import
|
|
338
368
|
# Return the number of valid lines and a list of all the invalid lines
|
|
339
369
|
@staticmethod
|
|
340
|
-
def validate_file_for_import(csv_file: io.TextIOWrapper, logger) ->
|
|
370
|
+
def validate_file_for_import(csv_file: io.TextIOWrapper, logger) -> tuple[int, list[str]]:
|
|
341
371
|
num_valid_lines = 0
|
|
342
372
|
invalid_lines = []
|
|
343
373
|
csv_file.seek(0) # set to start of file in case it has been read earlier
|
|
@@ -345,11 +375,11 @@ class UserItem(object):
|
|
|
345
375
|
while line and line != "":
|
|
346
376
|
try:
|
|
347
377
|
# do not print passwords
|
|
348
|
-
logger.info("Reading user {
|
|
378
|
+
logger.info(f"Reading user {line[:4]}")
|
|
349
379
|
UserItem.CSVImport._validate_import_line_or_throw(line, logger)
|
|
350
380
|
num_valid_lines += 1
|
|
351
381
|
except Exception as exc:
|
|
352
|
-
logger.info("Error parsing {
|
|
382
|
+
logger.info(f"Error parsing {line[:4]}: {exc}")
|
|
353
383
|
invalid_lines.append(line)
|
|
354
384
|
line = csv_file.readline()
|
|
355
385
|
return num_valid_lines, invalid_lines
|
|
@@ -358,7 +388,7 @@ class UserItem(object):
|
|
|
358
388
|
# Iterate through each field and validate the given value against hardcoded constraints
|
|
359
389
|
@staticmethod
|
|
360
390
|
def _validate_import_line_or_throw(incoming, logger) -> None:
|
|
361
|
-
_valid_attributes:
|
|
391
|
+
_valid_attributes: list[list[str]] = [
|
|
362
392
|
[],
|
|
363
393
|
[],
|
|
364
394
|
[],
|
|
@@ -373,23 +403,23 @@ class UserItem(object):
|
|
|
373
403
|
if len(line) > UserItem.CSVImport.ColumnType.MAX:
|
|
374
404
|
raise AttributeError("Too many attributes in line")
|
|
375
405
|
username = line[UserItem.CSVImport.ColumnType.USERNAME.value]
|
|
376
|
-
logger.debug("> details - {}"
|
|
406
|
+
logger.debug(f"> details - {username}")
|
|
377
407
|
UserItem.validate_username_or_throw(username)
|
|
378
408
|
for i in range(1, len(line)):
|
|
379
|
-
logger.debug("column {
|
|
409
|
+
logger.debug(f"column {UserItem.CSVImport.ColumnType(i).name}: {line[i]}")
|
|
380
410
|
UserItem.CSVImport._validate_attribute_value(
|
|
381
411
|
line[i], _valid_attributes[i], UserItem.CSVImport.ColumnType(i)
|
|
382
412
|
)
|
|
383
413
|
|
|
384
414
|
# Given a restricted set of possible values, confirm the item is in that set
|
|
385
415
|
@staticmethod
|
|
386
|
-
def _validate_attribute_value(item: str, possible_values:
|
|
416
|
+
def _validate_attribute_value(item: str, possible_values: list[str], column_type) -> None:
|
|
387
417
|
if item is None or item == "":
|
|
388
418
|
# value can be empty for any column except user, which is checked elsewhere
|
|
389
419
|
return
|
|
390
420
|
if item in possible_values or possible_values == []:
|
|
391
421
|
return
|
|
392
|
-
raise AttributeError("Invalid value {} for {}"
|
|
422
|
+
raise AttributeError(f"Invalid value {item} for {column_type}")
|
|
393
423
|
|
|
394
424
|
# https://help.tableau.com/current/server/en-us/csvguidelines.htm#settings_and_site_roles
|
|
395
425
|
# This logic is hardcoded to match the existing rules for import csv files
|
|
@@ -1,23 +1,76 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from requests import Response
|
|
4
|
-
from typing import Callable,
|
|
4
|
+
from typing import Callable, Optional
|
|
5
|
+
from collections.abc import Iterator
|
|
5
6
|
|
|
6
7
|
from defusedxml.ElementTree import fromstring
|
|
7
8
|
|
|
8
9
|
from tableauserverclient.datetime_helpers import parse_datetime
|
|
9
|
-
from .exceptions import UnpopulatedPropertyError
|
|
10
|
-
from .permissions_item import PermissionsRule
|
|
11
|
-
from .tag_item import TagItem
|
|
10
|
+
from tableauserverclient.models.exceptions import UnpopulatedPropertyError
|
|
11
|
+
from tableauserverclient.models.permissions_item import PermissionsRule
|
|
12
|
+
from tableauserverclient.models.tag_item import TagItem
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
class ViewItem
|
|
15
|
+
class ViewItem:
|
|
16
|
+
"""
|
|
17
|
+
Contains the members or attributes for the view resources on Tableau Server.
|
|
18
|
+
The ViewItem class defines the information you can request or query from
|
|
19
|
+
Tableau Server. The class members correspond to the attributes of a server
|
|
20
|
+
request or response payload.
|
|
21
|
+
|
|
22
|
+
Attributes
|
|
23
|
+
----------
|
|
24
|
+
content_url: Optional[str], default None
|
|
25
|
+
The name of the view as it would appear in a URL.
|
|
26
|
+
|
|
27
|
+
created_at: Optional[datetime], default None
|
|
28
|
+
The date and time when the view was created.
|
|
29
|
+
|
|
30
|
+
id: Optional[str], default None
|
|
31
|
+
The unique identifier for the view.
|
|
32
|
+
|
|
33
|
+
image: Optional[Callable[[], bytes]], default None
|
|
34
|
+
The image of the view. You must first call the `views.populate_image`
|
|
35
|
+
method to access the image.
|
|
36
|
+
|
|
37
|
+
name: Optional[str], default None
|
|
38
|
+
The name of the view.
|
|
39
|
+
|
|
40
|
+
owner_id: Optional[str], default None
|
|
41
|
+
The ID for the owner of the view.
|
|
42
|
+
|
|
43
|
+
pdf: Optional[Callable[[], bytes]], default None
|
|
44
|
+
The PDF of the view. You must first call the `views.populate_pdf`
|
|
45
|
+
method to access the PDF.
|
|
46
|
+
|
|
47
|
+
preview_image: Optional[Callable[[], bytes]], default None
|
|
48
|
+
The preview image of the view. You must first call the
|
|
49
|
+
`views.populate_preview_image` method to access the preview image.
|
|
50
|
+
|
|
51
|
+
project_id: Optional[str], default None
|
|
52
|
+
The ID for the project that contains the view.
|
|
53
|
+
|
|
54
|
+
tags: set[str], default set()
|
|
55
|
+
The tags associated with the view.
|
|
56
|
+
|
|
57
|
+
total_views: Optional[int], default None
|
|
58
|
+
The total number of views for the view.
|
|
59
|
+
|
|
60
|
+
updated_at: Optional[datetime], default None
|
|
61
|
+
The date and time when the view was last updated.
|
|
62
|
+
|
|
63
|
+
workbook_id: Optional[str], default None
|
|
64
|
+
The ID for the workbook that contains the view.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
|
|
15
68
|
def __init__(self) -> None:
|
|
16
69
|
self._content_url: Optional[str] = None
|
|
17
70
|
self._created_at: Optional[datetime] = None
|
|
18
71
|
self._id: Optional[str] = None
|
|
19
72
|
self._image: Optional[Callable[[], bytes]] = None
|
|
20
|
-
self._initial_tags:
|
|
73
|
+
self._initial_tags: set[str] = set()
|
|
21
74
|
self._name: Optional[str] = None
|
|
22
75
|
self._owner_id: Optional[str] = None
|
|
23
76
|
self._preview_image: Optional[Callable[[], bytes]] = None
|
|
@@ -29,15 +82,15 @@ class ViewItem(object):
|
|
|
29
82
|
self._sheet_type: Optional[str] = None
|
|
30
83
|
self._updated_at: Optional[datetime] = None
|
|
31
84
|
self._workbook_id: Optional[str] = None
|
|
32
|
-
self._permissions: Optional[Callable[[],
|
|
33
|
-
self.tags:
|
|
85
|
+
self._permissions: Optional[Callable[[], list[PermissionsRule]]] = None
|
|
86
|
+
self.tags: set[str] = set()
|
|
34
87
|
self._data_acceleration_config = {
|
|
35
88
|
"acceleration_enabled": None,
|
|
36
89
|
"acceleration_status": None,
|
|
37
90
|
}
|
|
38
91
|
|
|
39
92
|
def __str__(self):
|
|
40
|
-
return "<ViewItem {
|
|
93
|
+
return "<ViewItem {} '{}' contentUrl='{}' project={}>".format(
|
|
41
94
|
self._id, self.name, self.content_url, self.project_id
|
|
42
95
|
)
|
|
43
96
|
|
|
@@ -146,21 +199,21 @@ class ViewItem(object):
|
|
|
146
199
|
self._data_acceleration_config = value
|
|
147
200
|
|
|
148
201
|
@property
|
|
149
|
-
def permissions(self) ->
|
|
202
|
+
def permissions(self) -> list[PermissionsRule]:
|
|
150
203
|
if self._permissions is None:
|
|
151
204
|
error = "View item must be populated with permissions first."
|
|
152
205
|
raise UnpopulatedPropertyError(error)
|
|
153
206
|
return self._permissions()
|
|
154
207
|
|
|
155
|
-
def _set_permissions(self, permissions: Callable[[],
|
|
208
|
+
def _set_permissions(self, permissions: Callable[[], list[PermissionsRule]]) -> None:
|
|
156
209
|
self._permissions = permissions
|
|
157
210
|
|
|
158
211
|
@classmethod
|
|
159
|
-
def from_response(cls, resp: "Response", ns, workbook_id="") ->
|
|
212
|
+
def from_response(cls, resp: "Response", ns, workbook_id="") -> list["ViewItem"]:
|
|
160
213
|
return cls.from_xml_element(fromstring(resp), ns, workbook_id)
|
|
161
214
|
|
|
162
215
|
@classmethod
|
|
163
|
-
def from_xml_element(cls, parsed_response, ns, workbook_id="") ->
|
|
216
|
+
def from_xml_element(cls, parsed_response, ns, workbook_id="") -> list["ViewItem"]:
|
|
164
217
|
all_view_items = list()
|
|
165
218
|
all_view_xml = parsed_response.findall(".//t:view", namespaces=ns)
|
|
166
219
|
for view_xml in all_view_xml:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
2
|
import json
|
|
3
|
-
from typing import Callable,
|
|
3
|
+
from typing import Callable, Optional
|
|
4
|
+
from collections.abc import Iterable
|
|
4
5
|
from xml.etree.ElementTree import Element
|
|
5
6
|
|
|
6
7
|
from defusedxml.ElementTree import fromstring
|
|
@@ -23,7 +24,7 @@ class VirtualConnectionItem:
|
|
|
23
24
|
self._connections: Optional[Callable[[], Iterable[ConnectionItem]]] = None
|
|
24
25
|
self.project_id: Optional[str] = None
|
|
25
26
|
self.owner_id: Optional[str] = None
|
|
26
|
-
self.content: Optional[
|
|
27
|
+
self.content: Optional[dict[str, dict]] = None
|
|
27
28
|
self.certification_note: Optional[str] = None
|
|
28
29
|
|
|
29
30
|
def __str__(self) -> str:
|
|
@@ -40,7 +41,7 @@ class VirtualConnectionItem:
|
|
|
40
41
|
return self._id
|
|
41
42
|
|
|
42
43
|
@property
|
|
43
|
-
def permissions(self) ->
|
|
44
|
+
def permissions(self) -> list[PermissionsRule]:
|
|
44
45
|
if self._permissions is None:
|
|
45
46
|
error = "Workbook item must be populated with permissions first."
|
|
46
47
|
raise UnpopulatedPropertyError(error)
|
|
@@ -53,12 +54,12 @@ class VirtualConnectionItem:
|
|
|
53
54
|
return self._connections()
|
|
54
55
|
|
|
55
56
|
@classmethod
|
|
56
|
-
def from_response(cls, response: bytes, ns:
|
|
57
|
+
def from_response(cls, response: bytes, ns: dict[str, str]) -> list["VirtualConnectionItem"]:
|
|
57
58
|
parsed_response = fromstring(response)
|
|
58
59
|
return [cls.from_xml(xml, ns) for xml in parsed_response.findall(".//t:virtualConnection[@name]", ns)]
|
|
59
60
|
|
|
60
61
|
@classmethod
|
|
61
|
-
def from_xml(cls, xml: Element, ns:
|
|
62
|
+
def from_xml(cls, xml: Element, ns: dict[str, str]) -> "VirtualConnectionItem":
|
|
62
63
|
v_conn = cls(xml.get("name", ""))
|
|
63
64
|
v_conn._id = xml.get("id", None)
|
|
64
65
|
v_conn.webpage_url = xml.get("webpageUrl", None)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import xml.etree.ElementTree as ET
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
5
|
from defusedxml.ElementTree import fromstring
|
|
6
6
|
|
|
@@ -13,7 +13,40 @@ def _parse_event(events):
|
|
|
13
13
|
return NAMESPACE_RE.sub("", event.tag)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class WebhookItem
|
|
16
|
+
class WebhookItem:
|
|
17
|
+
"""
|
|
18
|
+
The WebhookItem represents the webhook resources on Tableau Server or
|
|
19
|
+
Tableau Cloud. This is the information that can be sent or returned in
|
|
20
|
+
response to a REST API request for webhooks.
|
|
21
|
+
|
|
22
|
+
Attributes
|
|
23
|
+
----------
|
|
24
|
+
id : Optional[str]
|
|
25
|
+
The identifier (luid) for the webhook. You need this value to query a
|
|
26
|
+
specific webhook with the get_by_id method or to delete a webhook with
|
|
27
|
+
the delete method.
|
|
28
|
+
|
|
29
|
+
name : Optional[str]
|
|
30
|
+
The name of the webhook. You must specify this when you create an
|
|
31
|
+
instance of the WebhookItem.
|
|
32
|
+
|
|
33
|
+
url : Optional[str]
|
|
34
|
+
The destination URL for the webhook. The webhook destination URL must
|
|
35
|
+
be https and have a valid certificate. You must specify this when you
|
|
36
|
+
create an instance of the WebhookItem.
|
|
37
|
+
|
|
38
|
+
event : Optional[str]
|
|
39
|
+
The name of the Tableau event that triggers your webhook.This is either
|
|
40
|
+
api-event-name or webhook-source-api-event-name: one of these is
|
|
41
|
+
required to create an instance of the WebhookItem. We recommend using
|
|
42
|
+
the api-event-name. The event name must be one of the supported events
|
|
43
|
+
listed in the Trigger Events table.
|
|
44
|
+
https://help.tableau.com/current/developer/webhooks/en-us/docs/webhooks-events-payload.html
|
|
45
|
+
|
|
46
|
+
owner_id : Optional[str]
|
|
47
|
+
The identifier (luid) of the user who owns the webhook.
|
|
48
|
+
"""
|
|
49
|
+
|
|
17
50
|
def __init__(self):
|
|
18
51
|
self._id: Optional[str] = None
|
|
19
52
|
self.name: Optional[str] = None
|
|
@@ -45,10 +78,10 @@ class WebhookItem(object):
|
|
|
45
78
|
|
|
46
79
|
@event.setter
|
|
47
80
|
def event(self, value: str) -> None:
|
|
48
|
-
self._event = "webhook-source-event-{}"
|
|
81
|
+
self._event = f"webhook-source-event-{value}"
|
|
49
82
|
|
|
50
83
|
@classmethod
|
|
51
|
-
def from_response(cls:
|
|
84
|
+
def from_response(cls: type["WebhookItem"], resp: bytes, ns) -> list["WebhookItem"]:
|
|
52
85
|
all_webhooks_items = list()
|
|
53
86
|
parsed_response = fromstring(resp)
|
|
54
87
|
all_webhooks_xml = parsed_response.findall(".//t:webhook", namespaces=ns)
|
|
@@ -61,7 +94,7 @@ class WebhookItem(object):
|
|
|
61
94
|
return all_webhooks_items
|
|
62
95
|
|
|
63
96
|
@staticmethod
|
|
64
|
-
def _parse_element(webhook_xml: ET.Element, ns) ->
|
|
97
|
+
def _parse_element(webhook_xml: ET.Element, ns) -> tuple:
|
|
65
98
|
id = webhook_xml.get("id", None)
|
|
66
99
|
name = webhook_xml.get("name", None)
|
|
67
100
|
|
|
@@ -82,4 +115,4 @@ class WebhookItem(object):
|
|
|
82
115
|
return id, name, url, event, owner_id
|
|
83
116
|
|
|
84
117
|
def __repr__(self) -> str:
|
|
85
|
-
return "<Webhook id={} name={} url={} event={}>"
|
|
118
|
+
return f"<Webhook id={self.id} name={self.name} url={self.url} event={self.event}>"
|