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.
Files changed (94) hide show
  1. tableauserverclient/__init__.py +33 -23
  2. tableauserverclient/{_version.py → bin/_version.py} +3 -3
  3. tableauserverclient/config.py +5 -3
  4. tableauserverclient/models/column_item.py +1 -1
  5. tableauserverclient/models/connection_credentials.py +18 -2
  6. tableauserverclient/models/connection_item.py +44 -6
  7. tableauserverclient/models/custom_view_item.py +78 -11
  8. tableauserverclient/models/data_acceleration_report_item.py +2 -2
  9. tableauserverclient/models/data_alert_item.py +5 -5
  10. tableauserverclient/models/data_freshness_policy_item.py +6 -6
  11. tableauserverclient/models/database_item.py +3 -3
  12. tableauserverclient/models/datasource_item.py +10 -10
  13. tableauserverclient/models/dqw_item.py +1 -1
  14. tableauserverclient/models/favorites_item.py +5 -6
  15. tableauserverclient/models/fileupload_item.py +1 -1
  16. tableauserverclient/models/flow_item.py +54 -9
  17. tableauserverclient/models/flow_run_item.py +3 -3
  18. tableauserverclient/models/group_item.py +44 -4
  19. tableauserverclient/models/groupset_item.py +4 -4
  20. tableauserverclient/models/interval_item.py +9 -9
  21. tableauserverclient/models/job_item.py +73 -8
  22. tableauserverclient/models/linked_tasks_item.py +5 -5
  23. tableauserverclient/models/metric_item.py +5 -5
  24. tableauserverclient/models/pagination_item.py +1 -1
  25. tableauserverclient/models/permissions_item.py +12 -10
  26. tableauserverclient/models/project_item.py +73 -19
  27. tableauserverclient/models/property_decorators.py +12 -11
  28. tableauserverclient/models/reference_item.py +2 -2
  29. tableauserverclient/models/revision_item.py +3 -3
  30. tableauserverclient/models/schedule_item.py +2 -2
  31. tableauserverclient/models/server_info_item.py +26 -6
  32. tableauserverclient/models/site_item.py +69 -3
  33. tableauserverclient/models/subscription_item.py +3 -3
  34. tableauserverclient/models/table_item.py +1 -1
  35. tableauserverclient/models/tableau_auth.py +115 -5
  36. tableauserverclient/models/tableau_types.py +2 -2
  37. tableauserverclient/models/tag_item.py +3 -4
  38. tableauserverclient/models/task_item.py +34 -4
  39. tableauserverclient/models/user_item.py +47 -17
  40. tableauserverclient/models/view_item.py +66 -13
  41. tableauserverclient/models/virtual_connection_item.py +6 -5
  42. tableauserverclient/models/webhook_item.py +39 -6
  43. tableauserverclient/models/workbook_item.py +116 -13
  44. tableauserverclient/namespace.py +1 -1
  45. tableauserverclient/server/__init__.py +2 -1
  46. tableauserverclient/server/endpoint/auth_endpoint.py +69 -10
  47. tableauserverclient/server/endpoint/custom_views_endpoint.py +258 -29
  48. tableauserverclient/server/endpoint/data_acceleration_report_endpoint.py +2 -2
  49. tableauserverclient/server/endpoint/data_alert_endpoint.py +14 -14
  50. tableauserverclient/server/endpoint/databases_endpoint.py +13 -12
  51. tableauserverclient/server/endpoint/datasources_endpoint.py +61 -62
  52. tableauserverclient/server/endpoint/default_permissions_endpoint.py +19 -18
  53. tableauserverclient/server/endpoint/dqw_endpoint.py +9 -9
  54. tableauserverclient/server/endpoint/endpoint.py +19 -21
  55. tableauserverclient/server/endpoint/exceptions.py +23 -7
  56. tableauserverclient/server/endpoint/favorites_endpoint.py +31 -31
  57. tableauserverclient/server/endpoint/fileuploads_endpoint.py +9 -11
  58. tableauserverclient/server/endpoint/flow_runs_endpoint.py +15 -13
  59. tableauserverclient/server/endpoint/flow_task_endpoint.py +2 -2
  60. tableauserverclient/server/endpoint/flows_endpoint.py +344 -29
  61. tableauserverclient/server/endpoint/groups_endpoint.py +342 -27
  62. tableauserverclient/server/endpoint/groupsets_endpoint.py +2 -2
  63. tableauserverclient/server/endpoint/jobs_endpoint.py +116 -7
  64. tableauserverclient/server/endpoint/linked_tasks_endpoint.py +2 -2
  65. tableauserverclient/server/endpoint/metadata_endpoint.py +2 -2
  66. tableauserverclient/server/endpoint/metrics_endpoint.py +10 -10
  67. tableauserverclient/server/endpoint/permissions_endpoint.py +13 -15
  68. tableauserverclient/server/endpoint/projects_endpoint.py +681 -30
  69. tableauserverclient/server/endpoint/resource_tagger.py +14 -13
  70. tableauserverclient/server/endpoint/schedules_endpoint.py +17 -18
  71. tableauserverclient/server/endpoint/server_info_endpoint.py +40 -5
  72. tableauserverclient/server/endpoint/sites_endpoint.py +282 -17
  73. tableauserverclient/server/endpoint/subscriptions_endpoint.py +10 -10
  74. tableauserverclient/server/endpoint/tables_endpoint.py +15 -14
  75. tableauserverclient/server/endpoint/tasks_endpoint.py +86 -8
  76. tableauserverclient/server/endpoint/users_endpoint.py +366 -19
  77. tableauserverclient/server/endpoint/views_endpoint.py +262 -20
  78. tableauserverclient/server/endpoint/virtual_connections_endpoint.py +6 -5
  79. tableauserverclient/server/endpoint/webhooks_endpoint.py +88 -11
  80. tableauserverclient/server/endpoint/workbooks_endpoint.py +653 -65
  81. tableauserverclient/server/filter.py +2 -2
  82. tableauserverclient/server/pager.py +29 -6
  83. tableauserverclient/server/query.py +68 -19
  84. tableauserverclient/server/request_factory.py +57 -37
  85. tableauserverclient/server/request_options.py +243 -141
  86. tableauserverclient/server/server.py +76 -10
  87. tableauserverclient/server/sort.py +16 -2
  88. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/METADATA +7 -7
  89. tableauserverclient-0.35.dist-info/RECORD +106 -0
  90. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/WHEEL +1 -1
  91. tableauserverclient-0.33.dist-info/RECORD +0 -106
  92. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE +0 -0
  93. {tableauserverclient-0.33.dist-info → tableauserverclient-0.35.dist-info}/LICENSE.versioneer +0 -0
  94. {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(object):
6
+ class TagItem:
8
7
  @classmethod
9
- def from_response(cls, resp: bytes, ns) -> Set[str]:
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) -> Set[str]:
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 List, Optional
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(object):
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) -> List["TaskItem"]:
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:{}".format(task_type), namespaces=ns)
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 Dict, List, Optional, TYPE_CHECKING, Tuple
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(object):
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[Dict[str, List]] = None
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={}>".format(self.id, self.name, str_site_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) -> Dict[str, List]:
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) -> List["UserItem"]:
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) -> List["UserItem"]:
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(object):
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: List[str] = list(map(str.strip, line.split(",")))
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) -> Tuple[int, List[str]]:
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 {}".format(line[:4]))
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 {}: {}".format(line[:4], exc))
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: List[List[str]] = [
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 - {}".format(username))
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 {}: {}".format(UserItem.CSVImport.ColumnType(i).name, line[i]))
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: List[str], column_type) -> None:
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 {}".format(item, column_type))
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, Iterator, List, Optional, Set
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(object):
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: Set[str] = set()
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[[], List[PermissionsRule]]] = None
33
- self.tags: Set[str] = set()
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 {0} '{1}' contentUrl='{2}' project={3}>".format(
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) -> List[PermissionsRule]:
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[[], List[PermissionsRule]]) -> None:
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="") -> List["ViewItem"]:
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="") -> List["ViewItem"]:
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, Dict, Iterable, List, Optional
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[Dict[str, dict]] = None
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) -> List[PermissionsRule]:
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: Dict[str, str]) -> List["VirtualConnectionItem"]:
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: Dict[str, str]) -> "VirtualConnectionItem":
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 List, Optional, Tuple, Type
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(object):
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-{}".format(value)
81
+ self._event = f"webhook-source-event-{value}"
49
82
 
50
83
  @classmethod
51
- def from_response(cls: Type["WebhookItem"], resp: bytes, ns) -> List["WebhookItem"]:
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) -> Tuple:
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={}>".format(self.id, self.name, self.url, self.event)
118
+ return f"<Webhook id={self.id} name={self.name} url={self.url} event={self.event}>"